#include "widget.h"
#include "ui_widget.h"
#include <time.h>
#include <QMessageBox>
#include <QPainter>
#include <QKeyEvent>
#include <QtDebug>
#include <algorithm>
#include <iostream>
#define Print(v) std::cout << #v << " : " << v << std::endl
//定义俄罗斯方块的图案
int block1[4][4]={
    {0, 0, 0, 0},
    {0, 1, 1, 0},
    {0, 1, 1 ,0},
    {0, 0, 0, 0}
};
//O型
int block2[4][4]={
    {0, 0, 1, 0},
    {0, 0, 1, 0},
    {0, 0, 1, 0},
    {0, 0, 1, 0}
};
//I型
int block3[4][4]={
    {0, 0, 0, 0},
    {0, 1, 1, 1},
    {0, 0, 1, 0},
    {0, 0, 0, 0}
};
//T型
int block4[4][4]={
    {0, 1, 0, 0},
    {0, 1, 0, 0},
    {0, 1, 1, 0},
    {0, 0, 0, 0}
};
//L
int block5[4][4]={
    {0, 0, 1, 0},
    {0, 0, 1, 0},
    {0, 1, 1, 0},
    {0, 0, 0, 0}
};
//J
int block6[4][4]={
    {0, 1, 0, 0},
    {0, 1, 1, 0},
    {0, 0, 1, 0},
    {0, 0, 0, 0}
};
//S
int block7[4][4]={
    {0, 0, 1, 0},
    {0, 1, 1, 0},
    {0, 1, 0, 0},
    {0, 0, 0, 0}
};
//Z

void Copy_Block(int Copy_Block[4][4],int Origin_Block[4][4]){
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<4;j++)
        {
            Copy_Block[i][j]=Origin_Block[i][j];
        }
    }
}


Widget::Widget(QWidget *parent):
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    resize(Col*Block_Size+Area_Out*4+4*Block_Size, Row*Block_Size+2*Area_Out);
    Init_Game();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    //游戏边框绘制
    painter.setBrush(QBrush(Qt::white, Qt::SolidPattern));
    painter.drawRect(Area_Out,Area_Out,Col*Block_Size,Row*Block_Size);
    //绘制下一个方块预告
    painter.setBrush(QBrush(Qt::blue,Qt::SolidPattern));
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<4;j++)
        {
            if(Next_Block[i][j]==1)
            {
                painter.drawRect(Area_Out*3+Col*Block_Size+j*Block_Size,Area_Out+i*Block_Size, Block_Size,Block_Size);
            }
        }
    }
    //绘制积分榜
    painter.setPen(Qt::black);
    painter.setFont(QFont("Arial", 14));
    painter.drawText(QRect(Area_Out*3+Col*Block_Size,Area_Out*2+4*Block_Size,4*Block_Size,4*Block_Size),Qt::AlignCenter,"score:"+QString::number(score));

    //绘制图像当中的方块
    for(int i=0;i<Row;i++)
    {
        for(int j=0;j<Col;j++)
        {
            //对正在运动的方块进行绘制
            if(Game_Area[i][j]==1)
            {
                painter.setBrush(QBrush(Qt::red,Qt::SolidPattern));
                painter.drawRect(j*Block_Size+Area_Out,i*Block_Size+Area_Out,Block_Size,Block_Size);
            }
            //对已经落入底部稳定形态的方块进行绘制
            else if(Game_Area[i][j]==2)
            {
                painter.setBrush(QBrush(Qt::green,Qt::SolidPattern));
                painter.drawRect(j*Block_Size+Area_Out,i*Block_Size+Area_Out,Block_Size,Block_Size);
            }
        }
    }
}

//游戏画面的改变
void Widget::timerEvent(QTimerEvent *event) {
    if(event->timerId()==Game_Timer)
        Block_Move(DOWN);
    //刷新画面
    if(event->timerId()==Paint_Timer)
    {
        update();
    }
}

//对按键进行响应
void Widget::keyPressEvent(QKeyEvent *event) {
    //Print((int)event->key());
    switch (event->key()) {
        case Qt::Key_Up:
            Block_Move(UP);
            //Print(event->key());
            break;
        case Qt::Key_Down:
            Block_Move(DOWN);
            //Print(event->key());
            break;
        case Qt::Key_Left:
            Block_Move(LEFT);
            //Print(event->key());
            break;
        case Qt::Key_Right:
            Block_Move(RIGHT);
            //Print(event->key());
            break;
        case Qt::Key_Space:
            Block_Move(SPACE);
            //Print(event->key());
            break;
        default:
            break;
    }
}

//创建方块实体
void Widget::Block_Crate(int Block[4][4], int Block_ID) {
    switch (Block_ID) {
        case 0:
            Copy_Block(Block, block1);
            break;
        case 1:
            Copy_Block(Block, block2);
            break;
        case 2:
            Copy_Block(Block, block3);
            break;
        case 3:
            Copy_Block(Block, block4);
            break;
        case 4:
            Copy_Block(Block,block5);
            break;
        case 5:
            Copy_Block(Block, block6);
            break;
        case 6:
            Copy_Block(Block, block7);
            break;
        default:
            break;
    }
}

//计算边界用
void Widget::Border_Side(int Block[4][4], Border &border) {
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<4;j++)
        {
            if(Block[i][j]==1)
            {
                border.down_bound=i;
                break;
            }
        }
    }
    for(int i=3;i>=0;i--)
    {
        for (int j = 0; j < 4; j++)
        {
            if(Block[i][j]==1)
            {
                border.up_bound=i;
                break;
            }
        }
    }
    for (int j = 0; j < 4; j++)
    {
        for (int i = 0; i < 4; i++)
        {
            if(Block[i][j]==1)
            {
                border.right_bound=j;
            }
        }
    }
    for (int j = 3; j >=0 ; j--)
    {
        for(int i=0;i<4;i++)
        {
            if(Block[i][j]==1)
            {
                border.left_bound=j;
                break;
            }
        }
    }
}

//初始化游戏
void Widget::Init_Game()
{
    for(int i=0;i<Row;i++)
    {
        for(int j=0;j<Col;j++)
        {
            Game_Area[i][j]=0;
        }
    }
    Down_Time=800;
    Refresh_Time=30;
    //以上数据定义见Widget.h

    //初始化随机数种子
    srand(time(0));

    //分数清零
    score=0;

    //运行游戏
    Start_Game();
}

void Widget::Block_Reset()
{
    //将当前方块加载进入界面
    Copy_Block(Now_Block, Next_Block);
    Border_Side(Now_Block, Now_border);

    //产生下一个方块
    int Block_ID=rand()%7;
    Block_Crate(Next_Block, Block_ID);

    //设置初始方块坐标，我们设定方块的左上角为坐标定点
    Block_Point start_point;
    start_point.x_pos=Col/2-2;
    start_point.y_pos=0;
    Pos_Block=start_point;
}

//开始游戏
void Widget::Start_Game()
{
    Game_Timer=startTimer(Down_Time);
    //开始游戏计时
    Paint_Timer=startTimer(Refresh_Time);
    //开启界面刷新计时器
    //产生初始下一个方块
    int Block_ID=rand()%7;
    Block_Crate(Next_Block, Block_ID);
    Block_Reset();
}

//结束游戏
void Widget::Game_Over()
{
    //结束后停止计时器
    killTimer(Game_Timer);
    killTimer(Paint_Timer);
    QMessageBox::information(this,"failed","game over");
}

//对方块进行旋转
void Widget::Block_Revolve(int Block[4][4])
{
    int Temp_Block[4][4];
    for (int i = 0; i < 4; i++)
    {
        for(int j=0;j<4;j++)
            Temp_Block[3-j][i]=Block[i][j];
    }
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<4;j++)
        {
            Block[i][j]=Temp_Block[i][j];
        }
    }
}

//将方块固定在屏幕内
void Widget::Block_Stable(int x, int y)
{
    for(int i=Now_border.up_bound;i<=Now_border.down_bound;i++)
    {
        for(int j=Now_border.left_bound;j<=Now_border.right_bound;j++)
        {
            if(Now_Block[i][j]==1)
                Game_Area[y+i][x+j]=2;
        }
    }
}

//确认是否方块撞击在其他方块上,并且操作方块运动在坐标上的显示
bool Widget::If_Crash(int x,int y,Direction dir)
{
    //用拷贝的临时方块做判断
    int Temp_Block[4][4];
    Copy_Block(Temp_Block,Now_Block);
    Border Temp_Border;
    Border_Side(Temp_Block,Temp_Border);
    //先尝试按照某方向走一格
    switch(dir)
    {
        case UP:
            Block_Revolve(Temp_Block);
            Border_Side(Temp_Block,Temp_Border); //旋转后要重新计算边界
            break;
            case DOWN:
                y+=1;
                break;
                case LEFT:
                    x-=1;
                    break;
                    case RIGHT:
                        x+=1;
                        break;
                        default:
                            break;
    }
    for(int i=Temp_Border.up_bound;i<=Temp_Border.down_bound;i++)
        for(int j=Temp_Border.left_bound;j<=Temp_Border.right_bound;j++)
            if(Game_Area[y+i][x+j]==2&&Temp_Block[i][j]==1||x+Temp_Border.left_bound<0||x+Temp_Border.right_bound>Col-1)
                return true;
            return false;
}

//方块移动
void Widget::Block_Move(Direction dir)
{
    switch (dir) {
        case UP:
            if(If_Crash(Pos_Block.x_pos,Pos_Block.y_pos,UP))
                break;
            //逆时针旋转90度
            Block_Revolve(Now_Block);
            //防止旋转后bug,i和j从0到4重新设置方块
            for(int i=0;i<4;i++)
                for(int j=0;j<4;j++)
                    Game_Area[Pos_Block.y_pos+i][Pos_Block.x_pos+j]=Now_Block[i][j];
                //重新计算边界
                Border_Side(Now_Block,Now_border);
                break;
                case DOWN:
                    //方块到达边界则不再移动
                    if(Pos_Block.y_pos+Now_border.down_bound==Row-1)
                    {
                        Block_Stable(Pos_Block.x_pos,Pos_Block.y_pos);
                        Block_Reset();
                        break;
                    }
                    //碰撞检测，只计算上下左右边界，先尝试走一格，如果碰撞则稳定方块后跳出
                    if(If_Crash(Pos_Block.x_pos,Pos_Block.y_pos,DOWN))
                    {
                        //只有最终不能下落才转成稳定方块
                        Block_Stable(Pos_Block.x_pos,Pos_Block.y_pos);
                        Block_Reset();
                        break;
                    }
                    //恢复方块上场景,为了清除移动过程中的方块残留
                    for(int j=Now_border.left_bound;j<=Now_border.right_bound;j++)
                        Game_Area[Pos_Block.y_pos][Pos_Block.x_pos+j]=0;
                    //没有碰撞则下落一格
                    Pos_Block.y_pos+=1;
                    //方块下降一格，拷贝到场景,注意左右边界
                    for(int i=0;i<4;i++) //必须是0到4而不是边界索引，考虑到旋转后边界重新计算
                        for(int j=Now_border.left_bound;j<=Now_border.right_bound;j++)
                            if(Pos_Block.y_pos+i<=Row-1&&Game_Area[Pos_Block.y_pos+i][Pos_Block.x_pos+j]!=2) //注意场景数组不越界,而且不会擦出稳定的方块
                                Game_Area[Pos_Block.y_pos+i][Pos_Block.x_pos+j]=Now_Block[i][j];
                            break;
                            case LEFT:
                                //到左边界或者碰撞不再往左
                                if(Pos_Block.x_pos+Now_border.left_bound==0||If_Crash(Pos_Block.x_pos,Pos_Block.y_pos,LEFT))
                                    break;
                                //恢复方块右场景,为了清除移动过程中的方块残留
                                for(int i=Now_border.up_bound;i<=Now_border.down_bound;i++)
                                    Game_Area[Pos_Block.y_pos+i][Pos_Block.x_pos+3]=0;
                                Pos_Block.x_pos-=1;
                                //方块左移一格，拷贝到场景
                                for(int i=Now_border.up_bound;i<=Now_border.down_bound;i++)
                                    for(int j=0;j<4;j++)
                                        if(Pos_Block.x_pos+j>=0&&Game_Area[Pos_Block.y_pos+i][Pos_Block.x_pos+j]!=2) //注意场景数组不越界
                                            Game_Area[Pos_Block.y_pos+i][Pos_Block.x_pos+j]=Now_Block[i][j];
                                        break;
                                        case RIGHT:
                                            if(Pos_Block.x_pos+Now_border.right_bound==Col-1||If_Crash(Pos_Block.x_pos,Pos_Block.y_pos,RIGHT))
                                                break;
                                            //恢复方块左场景,为了清除移动过程中的方块残留
                                            for(int i=Now_border.up_bound;i<=Now_border.down_bound;i++)
                                                Game_Area[Pos_Block.y_pos+i][Pos_Block.x_pos]=0;
                                            Pos_Block.x_pos+=1;
                                            //方块右移一格，拷贝到场景
                                            for(int i=Now_border.up_bound;i<=Now_border.down_bound;i++)
                                                for(int j=0;j<4;j++)
                                                    if(Pos_Block.x_pos+j<=Col-1&&Game_Area[Pos_Block.y_pos+i][Pos_Block.x_pos+j]!=2) //注意场景数组不越界
                                                        Game_Area[Pos_Block.y_pos+i][Pos_Block.x_pos+j]=Now_Block[i][j];
                                                    break;
                                                    case SPACE: //一次到底
                                                    //一格一格下移，直到不能下移
                                                    while(Pos_Block.y_pos+Now_border.down_bound<Row-1&&!If_Crash(Pos_Block.x_pos,Pos_Block.y_pos,DOWN))
                                                    {
                                                        //恢复方块上场景,为了清除移动过程中的方块残留
                                                        for(int j=Now_border.left_bound;j<=Now_border.right_bound;j++)
                                                            Game_Area[Pos_Block.y_pos][Pos_Block.x_pos+j]=0;
                                                        //没有碰撞则下落一格
                                                        Pos_Block.y_pos+=1;
                                                        //方块下降一格，拷贝到场景,注意左右边界
                                                        for(int i=0;i<4;i++) //必须是0到4
                                                            for(int j=Now_border.left_bound;j<=Now_border.right_bound;j++)
                                                                if(Pos_Block.y_pos+i<=Row-1&&Game_Area[Pos_Block.y_pos+i][Pos_Block.x_pos+j]!=2) //注意场景数组不越界,而且不会擦出稳定的方块
                                                                    Game_Area[Pos_Block.y_pos+i][Pos_Block.x_pos+j]=Now_Block[i][j];
                                                    }
                                                    Block_Stable(Pos_Block.x_pos,Pos_Block.y_pos);
                                                    Block_Reset();
                                                    break;
                                                    default:
                                                        break;
    }
    //处理消行，整个场景上面的行依次下移
    int i=Row-1;
    int line_count=0; //记消行数
    while(i>=1)
    {
        bool is_line_full=true;
        for(int j=0;j<Col;j++)
            if(Game_Area[i][j]==0)
            {
                is_line_full=false;
                i--;
                break;
            }
        if(is_line_full)
        {
            for(int k=i;k>=1;k--)
                for(int j=0;j<Col;j++)
                    Game_Area[k][j]=Game_Area[k-1][j];
                line_count++;//每次增加消行的行数
        }
    }
    score+=line_count*10; //得分
    //判断游戏是否结束
    for(int j=0;j<Col;j++)
        if(Game_Area[0][j]==2) //最顶端也有稳定方块
            Game_Over();
}

